#include <vector>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include "include/FightCtrl.h"
#include "include/Character.h"
#include "include/Pet.h"
#include "include/Scene.h"
#include "include/Formation.h"
#include "include/Utils.h"
#include "include/EventParser.h"

using namespace boost::property_tree;

void FightCtrl::FightMonster(GlobalGameObj *p_game_obj)
{
    int monster_id;
	string scene_id;

    try
    {
        monster_id = p_game_obj->in_tree.get<int>("monsterId");
		scene_id = p_game_obj->in_tree.get<string>("sceneId");
    }
	catch(exception &ex)
	{
		p_game_obj->error_code = Common::JsonTreeError;
		p_game_obj->error_str = "in FightMonster";
		return;
	}

	vector<Scene::MonsterInfo *> monster_info_list;
	Scene::SceneInfo scene_info;

	Scene::GetSceneInfo(p_game_obj, scene_id, &scene_info);
	if(0 != p_game_obj->error_code)
	{
		return;
	}

	Scene::GetMonsterInfo(scene_info.monster_str, monster_info_list);
	if(0 == monster_info_list.size())
	{
		p_game_obj->out_tree.put("code", 1);
		p_game_obj->out_tree.put("errorMsg", "场景中不存在此怪物");

		p_game_obj->out_format_tree.put("dataType", Common::NormalData);

		return;
	}

	int pos_x, pos_y;
	int has_monster = 0;
	for(int i = 0; i < monster_info_list.size(); i++)
	{
		if(monster_id == monster_info_list[i]->monster_id)
		{
			pos_x = monster_info_list[i]->x;
			pos_y = monster_info_list[i]->y;
			has_monster = 1;
		}

		// release monster info, importent!
		delete monster_info_list[i];
		monster_info_list[i] = 0;
	}

	if(0 == has_monster)
	{
		p_game_obj->out_tree.put("code", 1);
		p_game_obj->out_tree.put("errorMsg", "场景中不存在此怪物");

		p_game_obj->out_format_tree.put("dataType", Common::NormalData);

		return;
	}

	Scene::SceneObj scene_obj;
	scene_obj.scene_id = scene_id;
	scene_obj.obj_type = Scene::SceneObjCharacter;
	scene_obj.obj_id = p_game_obj->character_id;

	Scene::GetSceneObj(p_game_obj, &scene_obj);
	if(0 != p_game_obj->error_code)
	{
		return;
	}

	if((scene_obj.x - pos_x) > 50 || (scene_obj.x - pos_x) < -50)
	{
		p_game_obj->out_tree.put("code", 1);
		p_game_obj->out_tree.put("errorMsg", "距离怪物太远");

		p_game_obj->out_format_tree.put("dataType", Common::NormalData);

		return;
	}

	Formation::FormationInfo my_formation_info;
	Formation::GetFormation(p_game_obj, Formation::TypePlayer, p_game_obj->character_id, &my_formation_info);
	if(0 != p_game_obj->error_code)
	{
		return;
	}

	Formation::FormationInfo monster_formation_info;
	Formation::GetFormation(p_game_obj, Formation::TypeMonster, monster_id, &monster_formation_info);
	if(0 != p_game_obj->error_code)
	{
		return;
	}

	// begin fight
	
	// init self fight info
	Formation::ActorInfo actor_info;

	vector<Fight::FightInfo *> my_fight_list;

	for(int i = 1; i <= FORMATION_POS_NUM; i++)
	{
		string pos_str = Formation::GetPosStr(&my_formation_info, i);
		Formation::PosStr2ActorInfo(pos_str, &actor_info);

		if(0 == actor_info.id)
		{
			continue;
		}

		Fight::FightInfo *p_fight_info = new Fight::FightInfo();
		p_fight_info->pos = actor_info.pos;
		p_fight_info->type = actor_info.type;
		p_fight_info->fighter_id = actor_info.id;
		p_fight_info->dander = GetInitDander(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->buffer_str = GetBuffer(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->hp = GetHp(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->skill_id_str = GetSkillStr(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->buffer_start_time_str = GetBufferStartTime(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->tmp_buffer_str = "";
		p_fight_info->tmp_buffer_start_time_str = "";

		my_fight_list.push_back(p_fight_info);
	}

	vector<Fight::FightInfo *> monster_fight_list;

	for(int i = 1; i <= FORMATION_POS_NUM; i++)
	{
		string pos_str = Formation::GetPosStr(&monster_formation_info, i);
		Formation::PosStr2Actor(pos_str, &actor_info);

		if(0 == actor_info.id)
		{
			continue;
		}
		
		Fight::FightInfo *p_fight_info = new Fight::FightInfo();
		p_fight_info->pos = actor_info.pos;
		p_fight_info->type = actor_info.type;
		p_fight_info->fighter_id = actor_info.id;
		p_fight_info->dander = GetInitDander(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->buffer_str = GetBuffer(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->hp = GetHp(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->skill_id_str = GetSkillStr(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->buffer_start_time_str = GetBufferStartTime(p_fight_info->type, p_fight_info->fighter_id);
		p_fight_info->tmp_buffer_str = "";
		p_fight_info->tmp_buffer_start_time_str = "";

		monster_fight_list.push_back(p_fight_info);
	}

	int my_index = 0;
	int enemy_index = 0;
	int my_first_attack;
	int enemy_first_attack;
	int fight_result;

	for(int i = 0; i < MAX_FIGHT_TURN; i++)
	{
		my_first_attack = CalculateFirstAttack(my_formation_info);
		enemy_first_attack = CalculateFirstAttack(monster_formation_info);

		if(my_first_attack >= enemy_first_attack)
		{
			fight_result = FightTurn(my_index, my_fight_list, enemy_index, monster_fight_list);
		}
		else
		{
			fight_result = FightTurn(enemy_index, monster_fight_list, my_index, my_fight_list);
		}

		if(FIGHT_RESULT_CONTINUE != fight_result)
		{
			break;
		}
	}
}

int FightCtrl::GetNextFighter(GlobalGameObj *p_game_obj, vector<Fight::FightInfo *> &fighter_list, int begin_index)
{
	for(int i = begin_index; i < fighter_list.size(); i++)
	{
		if(fighter_list[i]->hp <= 0)
		{
			continue;
		}
		
		if(!Buffer::CanAttack(p_game_obj, fighter_list[i]->tmp_buffer_str, fighter_list[i]->tmp_buffer_start_time_str))
		{
			continue;
		}

		return i;
	}

	return -1;
}

int FightCtl::GetAvailableSkill(Fight::FightInfo *p_fight_info)
{
	if(p_fight_info->skill_id_str == "")
	{
		return 0;
	}
	
	vector<string> skill_id_arr;
	vector<string> buffer_arr;
	int skill_id;

	Buffer::GetValidBuffer(p_fight_info->tmp_buffer_str, p_fight_info->tmp_buffer_start_time_str,
		buffer_arr);

	Utils::Str2StrList(p_fight_info->skill_id_str, skill_id_arr, '^');
	for(int i = skill_id_arr.size() - 1; i >= 0; i--)
	{
		skill_id = atoi(skill_id_arr[i]);
		if(Skill::CanPerformSkill(skill_id, p_fight_info->dander, buffer_arr))
		{
			return skill_id;
		}
	}

	return 0;
}

int FightCtrl::FightTurn(GlobalGameObj *p_game_obj, int index1, vector<Fight::FightInfo *> &fighter_list1,
		int index2, vector<Fight::FightInfo *> &fighter_list2)
{
	vector<int> index_list;
	int skill_id;

	index1 = GetNextFighter(p_game_obj, fighter_list1, index1);
	if(-1 != index1)
	{
		skill_id = GetAvailableSkill(fighter_list1[index1]);
		if(0 != skill_id)
		{
			PerformSkill(skill_id, fighter_list1[index1]->pos, fighter_list2, index_list);
		}
		else
		{
			AttackNormal(fighter_list1[index1]->pos, fighter_list2, index_list);
		}
	}

	index_list.clear();

	index2 = GetNextFighter(fighter_list2, index2);
	if(-1 != index2)
	{
		skill_id = GetAvailableSkill(fighter_list2[index2]);
		if(-1 != skill_id)
		{
			DoSkill(skill_id, fighter_list2[index2]->pos, fighter_list1, index_list);
		}
	}

	if(-1 == index1 && -1 == index2)
	{
		return 1;
	}

	return 0;
}

